#!/bin/bash
GNAPVERSION='2.0'

GNAPNAME=$(basename "$0")
echo "GNAP Core Building tool ${GNAPNAME} version ${GNAPVERSION}"
GNAPLIBDIR='/usr/lib/gnap'
STAGE3FILE="${GNAPLIBDIR}/gnap-stage3seed.tar.bz2"
SNAPSHOTFILE="${GNAPLIBDIR}/gnap-portagesnapshot.tar.bz2"
SPECS="${GNAPLIBDIR}/gnap-specs.tar.bz2"
TEMPDIR=''

G=$'\e[32;01m'
B=$'\e[31;01m'
N=$'\e[0m'
W=$'\e[33;01m'
K=$'\e[34;01m'
C="$[$(set -- $(stty size 2>/dev/null); echo ${2}) - 7]"
E=$'\e['${C}'G'

gwarn() {
	echo -e " ${W}*${N} ${*}"
}

gconfirm() {
	if [[ "${FORCEYES}" -eq 1 ]]; then
		gwarn "${*} forced to yes"
	else
		read -ep " ${W}*${N} ${*} [N]: " answer
		if [[ "${answer}" != 'y' && "${answer}" != 'Y' ]]; then
			if [[ -n "${TEMPDIR}" ]]; then
				cleanup
			fi
			echo Build aborted !
			exit 2
		fi
	fi
}

gbegin() {
	echo -ne " ${G}*${N} ${*}..."
}

gtest() {
	continued=0
	if [[ "$#" -gt 0 && "${1}" == 'continued' ]]; then
		shift
		continued=1
	fi
	if [[ "$#" -eq 0 || "${1}" -eq 0 ]]; then
		if [[ "${continued}" -eq 0 ]]; then
			echo -e "${E}  ${K}[ ${G}ok${K} ]${N}"
		fi
	else
		echo -e "${E}  ${K}[ ${B}!!${K} ]${N}"
		if [[ "$#" -ge 2 ]]; then
			shift
			echo -en " ${B}*${N} ${*}"
			echo -e "${E}  ${K}[ ${B}!!${K} ]${N}"
		fi
		if [[ -n "${TEMPDIR}" ]]; then
			cleanup
		fi
		echo "Build failed, try man ${GNAPNAME} for more help"
		exit 1
	fi
}

usage() {
	echo 'Options:'
	echo '    -t stage             Build stage to execute (or "all")'
	echo '    -o overlay_dir       Portage overlay directory'
	echo '    -v version_stamp     GNAP timestamp'
	echo '    -f                   Force all answers to yes'
	echo '    -s seedstagefile     File to use as a Stage3 seed tarball'
	echo '    -p portage_snapshot  File to use as Portage snapshot'
	echo '    -l logfile           Use specific log file prefix'
	echo '    -c catalyst.conf     Use specific catalyst.conf file'
	echo '    -e specs             Specs directory or tar.bz2 file'
	echo
	echo "Please man ${GNAPNAME} for more details."
}

cleanup() {
	gbegin 'Cleaning temporary directories'
	if [[ -d "${TEMPDIR}" ]]; then
		DIRTOREMOVE="${TEMPDIR}"
		TEMPDIR=''
		rm -rf "${DIRTOREMOVE}"
		gtest $? "Failed to remove ${DIRTOREMOVE}"
	else
		gtest 0
	fi
}

if [[ "$#" -eq 0 || "${1}" == '-h' ]]; then
	usage
	exit 0
fi

gbegin 'Checking parameters'

# Read options
NOTARGET=1
STAMP=$(date +%Y%m%d)
while getopts ':hs:p:m:o:v:t:fl:c:e:' options; do
	case ${options} in
		h ) usage
			exit 0;;
		s ) STAGE3FILE="${OPTARG}";;
		p ) SNAPSHOTFILE="${OPTARG}";;
		o ) PORTAGE_OVERLAYS="${PORTAGE_OVERLAYS} ${OPTARG}";;
		v ) STAMP="${OPTARG}";;
		t )
			case "${OPTARG}" in
				all ) ALLTARGET=1;;
				stage3 )
					STAGE3=1
					NEEDS_SNAPSHOT=1;;
				livecd-stage1 )
					LIVECD1=1
					NEEDS_SNAPSHOT=1;;
				livecd-stage2 )
					LIVECD2=1
					NEEDS_SNAPSHOT=1;;
				tarball ) TARBALL=1;;
				extensions )
					MODULES=1
					NEEDS_SNAPSHOT=1;;
			esac
			NOTARGET=0;;
		f ) FORCEYES=1;;
		l ) GNAPLOGPREFIX="${OPTARG}";;
		c ) CATALYST_CONF="${OPTARG}";;
		e ) SPECS="${OPTARG}";;
		* ) gtest 1 'Specified options are incomplete or unknown !';;
	esac
done

# Root is needed
test "${EUID}" -eq 0
gtest continued $? "You need to be root to run ${GNAPNAME}"

# Setting up temporary directory
TEMPDIR=$(mktemp -d -t gnap_make.XXXXXX)
gtest continued $? 'Failed to create temporary directory'

# Prepare specs dir and check common.conf file
SPECDIR="${TEMPDIR}/specs"
if [[ -f "${SPECS}" ]]; then
	mkdir "${SPECDIR}"
	gtest continued $? 'Failed to create specs temporary subdirectory'
	tar jx -f "${SPECS}" -C "${SPECDIR}"
	gtest continued $? 'Failed to unpack specs'
elif [[ -d "${SPECS}" ]]; then
	cp -rp "${SPECS}" "${SPECDIR}"
	gtest continued $? 'Failed to copy specs directory contents'
else
	gtest continued 1 "${SPECS} not found, provide a valid -e option"
fi
test -f "${SPECDIR}/common.conf"
gtest continued $? "Incorrect specdir: ${SPECDIR}/common.conf not found !"
source "${SPECDIR}/common.conf"
export CHOST
export CFLAGS
export CXXFLAGS
if [[ -n "${DISTCC_HOSTS}" ]]; then
	DISTCCSPEC="distcc_hosts: ${DISTCC_HOSTS}"
fi

# catalyst.conf file
test -f "${CATALYST_CONF}"
gtest continued $? "${CATALYST_CONF} file not found !"
source "${CATALYST_CONF}"

# Default targets is complete core build
if [[ "${ALLTARGET}" -eq 1 ]]; then
	STAGE3=1
	LIVECD1=1
	LIVECD2=1
	TARBALL=1
	MODULES=1
	NEEDS_SNAPSHOT=1
fi

# At least one target is needed
test "${NOTARGET}" -eq 0
gtest continued $? \
	'No target specified. You should provide at least one -t option.'

# CATALYST_DIR must exist
if [[ ! -d "${CATALYST_DIR}" ]]; then
  mkdir "${CATALYST_DIR}"
  gtest continued $? \
	"Error: failed to create ${CATALYST_DIR} directory."
fi

# Stage3 needs a seed stage
if [[ "${STAGE3}" -eq 1 ]]; then
	test -f "${STAGE3FILE}"
	gtest continued $? 'The "-s" option needs to designate a valid seed stage'
fi

# Snapshot must exist if a stage is selected
if [[ "${NEEDS_SNAPSHOT}" -eq 1 ]]; then
	test -f "${SNAPSHOTFILE}"
	gtest continued $? "Can't find ${SNAPSHOTFILE}"
fi

# Seed stage if needed must be an existing file
if [[ "${STAGE3}" -eq 1 ]]; then
	test -f "${STAGE3FILE}"
	gtest continued $? "${STAGE3FILE} is not a valid stage3 tarball"
fi
gtest 0

# If extensions and no stage3, warn that we'll use seedstage as stage3
STAGE3LOC="${CATALYST_DIR}/builds/${RELTYPE}/stage3-${SUBARCH}-${STAMP}.tar.bz2"
if [[ "${MODULES}" -eq 1 || "${LIVECD1}" -eq 1 ]]; then
	if [[ "${STAGE3}" -ne 1 && ! -f "${STAGE3LOC}" ]]; then
		gwarn '"livecd-stage1" or "extensions" was selected without "stage3".'
		gconfirm 'Should I use the seed stage as stage3 result ?'
		if [[ ! -d "${CATALYST_DIR}/builds/${RELTYPE}" ]]; then
			mkdir -p "${CATALYST_DIR}/builds/${RELTYPE}"
		fi
		cp "${STAGE3FILE}" "${STAGE3LOC}"
	fi
fi

# Explain what will get built
if [[ "${STAGE3}" -eq 1 ]]; then
	TARGETLIST='[stage3] '
fi
if [[ "${LIVECD1}" -eq 1 ]]; then
	TARGETLIST="${TARGETLIST}[livecd-stage1] "
fi
if [[ "${LIVECD2}" -eq 1 ]]; then
	TARGETLIST="${TARGETLIST}[livecd-stage2] "
fi
if [[ "${TARBALL}" -eq 1 ]]; then
	TARGETLIST="${TARGETLIST}[tarball] "
fi
if [[ "${MODULES}" -eq 1 ]]; then
	TARGETLIST="${TARGETLIST}[extensions]"
fi
gwarn 'The following targets will be called:'
gwarn "${TARGETLIST}"

# Confirm tarball overwrite if TARBALL stage selected
if [[ "${TARBALL}" -eq 1 ]]; then
	if [[ -e "gnap-${GNAPVERSION}-${STAMP}.tar" ]]; then
		gconfirm "gnap-${GNAPVERSION}-${STAMP}.tar already exists, overwrite"
	fi
fi

# Logfile setup and confirmation
if [[ -z "$GNAPLOGPREFIX" ]]; then
	GNAPLOGPREFIX="./${GNAPNAME}-${STAMP}"
fi

if [[ -f "${GNAPLOGPREFIX}.out" || -f "${GNAPLOGPREFIX}.err" ]]; then
	if [[ "${FORCEYES}" -ne 1 ]]; then
		read -ep \
			" ${W}*${N} Logfile(s) already exists. Append/Overwrite [A]: " \
			answer
		if [[ "${answer}" == 'o' || "${answer}" == 'O' ]]; then
			rm "${GNAPLOGPREFIX}.out" "${GNAPLOGPREFIX}.err"
		fi
	fi
fi
touch "${GNAPLOGPREFIX}.out"
touch "${GNAPLOGPREFIX}.err"
SEELOGFILES="see ${GNAPLOGPREFIX}.err and .out for details"

# Snapshot preparation
if [[ "${NEEDS_SNAPSHOT}" -eq 1 ]]; then
	gbegin 'Preparing portage snapshot'

	if [[ ! -d "${CATALYST_DIR}/snapshots" ]]; then
		mkdir -p "${CATALYST_DIR}/snapshots"
	fi

	if [[ -z "${PORTAGE_OVERLAYS}" ]]; then
		cp "${SNAPSHOTFILE}" "${CATALYST_DIR}/snapshots/portage-${STAMP}.tar.bz2"
		gtest $? "Snapshot preparation failed, ${SEELOGFILES}"
	else
		TEMPPRTDIR="${TEMPDIR}/portage"
		mkdir "${TEMPPRTDIR}"
		gtest continued $? 'Failed to create portage temporary subdirectory'

		tar jxf "${SNAPSHOTFILE}" -C "${TEMPPRTDIR}" \
			>> "${GNAPLOGPREFIX}.out" 2>> "${GNAPLOGPREFIX}.err"

		for overlay in ${PORTAGE_OVERLAYS} ; do
			cp -rp ${overlay}/* "${TEMPPRTDIR}/portage"
			gtest continued $? "Failed to copy ${overlay}"
		done

		tar jcf "${CATALYST_DIR}/snapshots/portage-${STAMP}.tar.bz2" \
			-C "${TEMPPRTDIR}" . \
			>> "${GNAPLOGPREFIX}.out" 2>> "${GNAPLOGPREFIX}.err"
		gtest $? "Snapshot preparation failed, ${SEELOGFILES}"
	fi
fi

# Stage3 phase
if [[ "${STAGE3}" -eq 1 ]]; then
	gbegin "${G}[stage3]${N} stage (base system build)"

	if [[ ! -d "${CATALYST_DIR}/builds/${RELTYPE}" ]]; then
		mkdir -p "${CATALYST_DIR}/builds/${RELTYPE}"
	fi
	cp "${STAGE3FILE}" "${CATALYST_DIR}/builds/${RELTYPE}/seedstage.tar.bz2"

	TEMPCONF="${TEMPDIR}/stage3.conf"
	touch "${TEMPCONF}"
	gtest continued $? 'Failed to create stage3 temporary conf file'
	cat >> "${TEMPCONF}" <<EOF
target: stage3
${DISTCCSPEC}
subarch: ${SUBARCH}
rel_type: ${RELTYPE}
snapshot: ${STAMP}
version_stamp: ${STAMP}
profile: ${PROFILE}
source_subpath: ${RELTYPE}/seedstage
portage_confdir: ${SPECDIR}/portage_confdir
EOF
	$CATALYST_BIN -c "${CATALYST_CONF}" -f "${TEMPCONF}" \
		>> "${GNAPLOGPREFIX}.out" 2>> "${GNAPLOGPREFIX}.err"
	gtest $? "[stage3] failed, ${SEELOGFILES}"
	rm "${CATALYST_DIR}/builds/${RELTYPE}/seedstage.tar.bz2"
fi

# LIVECD-STAGE1 phase
if [[ "${LIVECD1}" -eq 1 ]]; then
	gbegin "${G}[livecd-stage1]${N} stage (GNAP-specific packages build)"
	TEMPCONF="${TEMPDIR}/livecd-stage1.conf"
	touch "${TEMPCONF}"
	gtest continued $? 'Failed to create livecd-stage1 temporary conf file'
	cat >> "${TEMPCONF}" <<EOF
target: livecd-stage1
${DISTCCSPEC}
subarch: ${SUBARCH}
rel_type: ${RELTYPE}
snapshot: ${STAMP}
version_stamp: ${STAMP}
profile: ${PROFILE}
source_subpath: ${RELTYPE}/stage3-${SUBARCH}-${STAMP}
portage_confdir: ${SPECDIR}/portage_confdir
EOF
	cat "${SPECDIR}/packages.conf" >> "${TEMPCONF}"

	$CATALYST_BIN -c "${CATALYST_CONF}" -f "${TEMPCONF}" \
		>> "${GNAPLOGPREFIX}.out" 2>> "${GNAPLOGPREFIX}.err"
	gtest $? "[livecd-stage1] failed, ${SEELOGFILES}"
fi

# LIVECD-STAGE2 phase
if [[ "${LIVECD2}" -eq 1 ]]; then
	gbegin "${G}[livecd-stage2]${N} stage (kernel and LiveCD builds)"
	TEMPCONF="${TEMPDIR}/livecd-stage2.conf"
	touch "${TEMPCONF}"
	gtest continued $? 'Failed to create livecd-stage2 temporary conf file'
	cat >> "${TEMPCONF}" <<EOF
target: livecd-stage2
${DISTCCSPEC}
subarch: ${SUBARCH}
rel_type: ${RELTYPE}
snapshot: ${STAMP}
version_stamp: ${STAMP}
profile: ${PROFILE}
source_subpath: ${RELTYPE}/livecd-stage1-${SUBARCH}-${STAMP}
boot/kernel/gentoo/sources: ${KERNEL_SOURCES}
boot/kernel/gentoo/config: ${SPECDIR}/kernel.config
boot/kernel/gentoo/extraversion: GNAP-${GNAPVERSION}
livecd/iso: gnap-${GNAPVERSION}-${STAMP}.iso
livecd/fsscript: ${SPECDIR}/fsscript
livecd/root_overlay: ${SPECDIR}/root_overlay
EOF
	cat "${SPECDIR}/livecd.conf" >> "${TEMPCONF}"

	$CATALYST_BIN -c "${CATALYST_CONF}" -f "${TEMPCONF}" \
		>> "${GNAPLOGPREFIX}.out" 2>> "${GNAPLOGPREFIX}.err"
	gtest $? "[livecd-stage2] failed, ${SEELOGFILES}"
fi

# TARBALL phase
if [[ "${TARBALL}" -eq 1 ]]; then
	gbegin "${G}[tarball]${N} phase (Creation of core and basefs components)"
	test -e "gnap-${GNAPVERSION}-${STAMP}.iso"
	gtest continued $? "No gnap-${GNAPVERSION}-${STAMP}.iso file to convert !"
	test -d "${CATALYST_DIR}/tmp/gnap/livecd-stage1-${SUBARCH}-${STAMP}"
	gtest $? 'Missing livecd-stage2 results'

	gbegin '  Creating core component'
	TEMPMNTDIR="${TEMPDIR}/mount"
	mkdir "${TEMPMNTDIR}"
	gtest continued $? 'Failed to create mount temporary subdirectory'
	TEMPISODIR="${TEMPDIR}/iso"
	mkdir "${TEMPISODIR}"
	gtest continued $? 'Failed to create iso temporary subdirectory'

	mount -o loop "gnap-${GNAPVERSION}-${STAMP}.iso" ${TEMPMNTDIR} && \
		cp -r ${TEMPMNTDIR}/* ${TEMPISODIR}
	gtest continued $? 'Failed to mount ISO and copy files'
	umount "${TEMPMNTDIR}"
	gtest continued $? "Failed to unmount ${TEMPMNTDIR}"

	cp "${SPECDIR}/isolinux/isolinux.cfg" ${TEMPISODIR}/isolinux/
	cp "${SPECDIR}/isolinux/syslinux.cfg" ${TEMPISODIR}/
	cp "${SPECDIR}/isolinux/boot.msg" ${TEMPISODIR}/isolinux/
	DATE=$(date --utc)
	echo "GNAP-${GNAPVERSION}-${STAMP} built on ${DATE}" \
		>> "${TEMPISODIR}/isolinux/boot.msg"

	tar cf "gnap-${GNAPVERSION}-${STAMP}.tar" -C "${TEMPISODIR}" .
	gtest $? 'Failed to create tarball'
	rm "gnap-${GNAPVERSION}-${STAMP}.iso"

	gbegin '  Creating basefs component'
	tar jcf "gnap-basefs-${GNAPVERSION}-${STAMP}.tar.bz2" \
		-C "${CATALYST_DIR}/tmp/gnap/livecd-stage2-${SUBARCH}-${STAMP}" .
	gtest $? 'Unable to create basefs tarball'
fi

# EXTENSIONS phase
if [[ "${MODULES}" -eq 1 ]]; then
	gbegin "${G}[extensions]${N} stage start"
	GRP_PREFIX="${CATALYST_DIR}/builds/${RELTYPE}/grp-${SUBARCH}-${STAMP}"
	SPECMODULE="${SPECDIR}/extensions.conf"
	mod_list=$(grep '^extensions:' "${SPECMODULE}" 2>/dev/null);
	mod_list="${mod_list/extensions:/}"
	test -n "${mod_list}"
	gtest $? 'No extension to build'
	for mod_name in $mod_list ; do
		gbegin "  Building ${mod_name} extension"
		mod_useflags=$(grep "^${mod_name}/useflags:" ${SPECMODULE} 2>/dev/null);
		mod_useflags="${mod_useflags/${mod_name}\/useflags:/}"
		mod_packlist=$(grep "^${mod_name}/packlist:" ${SPECMODULE} 2>/dev/null);
		mod_packlist="${mod_packlist/${mod_name}\/packlist:/}"
		mod_cleanup=$(grep "^${mod_name}/cleanup:" ${SPECMODULE} 2>/dev/null);
		mod_cleanup="${mod_cleanup/${mod_name}\/cleanup:/}"
		TEMPCONF="${TEMPDIR}/ext-${mod_name}.conf"
		touch "${TEMPCONF}"
		gtest continued $? 'Failed to create extension temporary conf file'
		cat >> $TEMPCONF <<EOF
target: grp
${DISTCCSPEC}
subarch: ${SUBARCH}
rel_type: ${RELTYPE}
snapshot: ${STAMP}
version_stamp: ${STAMP}
profile: ${PROFILE}
source_subpath: ${RELTYPE}/stage3-${SUBARCH}-${STAMP}
grp: ${mod_name}
grp/use: ${mod_useflags}
grp/${mod_name}/type: pkgset
grp/${mod_name}/packages: ${mod_packlist}
EOF

		$CATALYST_BIN -c "${CATALYST_CONF}" -f "${TEMPCONF}" \
			>> "${GNAPLOGPREFIX}.out" 2>> "${GNAPLOGPREFIX}.err"
		gtest continued $? \
			"Extension build failed, ${SEELOGFILES}"

		TEMPMODULEDIR="${TEMPDIR}/module_${mod_name}"
		mkdir "${TEMPMODULEDIR}"
		gtest continued $? 'Failed to create module temporary subdirectory'
		for pkg in $( ls ${GRP_PREFIX}/${mod_name}/All/*.tbz2 ); do
			tar jxf $pkg -C "${TEMPMODULEDIR}" -p \
				>> "${GNAPLOGPREFIX}.out" 2>> "${GNAPLOGPREFIX}.err"
		done
		gtest continued $? 'Failed to unpack extension packages'

		mod_rmlist=""
		for cleanup in ${mod_cleanup} ; do
			cleanup=${cleanup/\.\./}
			mod_rmlist="${mod_rmlist} ${TEMPMODULEDIR}/${cleanup}"
		done
		rm -rf ${mod_rmlist}
		gtest continued $? 'Failed to apply extension cleanup instructions'

		tar jcf "gnapext_${mod_name}-${STAMP}.tbz2" \
			-C "${TEMPMODULEDIR}" . \
			>> "${GNAPLOGPREFIX}.out" 2>> "${GNAPLOGPREFIX}.err"
		gtest $? 'Failed to build extension file'
	done
fi

cleanup
echo 'Build successful !'
exit 0
